package com.xuecheng.media.service.jobhandler;

import com.xuecheng.base.utils.Mp4VideoUtil;
import com.xuecheng.media.model.po.MediaProcess;
import com.xuecheng.media.service.MediaFileProcessService;
import com.xuecheng.media.service.MediaFileService;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.*;

/**
 * XxlJob开发示例（Bean模式）
 *
 * 开发步骤：
 *      1、任务开发：在Spring Bean实例中，开发Job方法；
 *      2、注解配置：为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")"，注解value值对应的是调度中心新建任务的JobHandler属性的值。
 *      3、执行日志：需要通过 "XxlJobHelper.log" 打印执行日志；
 *      4、任务结果：默认任务结果为 "成功" 状态，不需要主动设置；如有诉求，比如设置任务结果为失败，可以通过 "XxlJobHelper.handleFail/handleSuccess" 自主设置任务结果；
 *
 * @author xuxueli 2019-12-11 21:52:51
 */
@Slf4j
@Component
public class VideoTask {
    private static Logger logger = LoggerFactory.getLogger(VideoTask.class);
    @Autowired
    public MediaFileProcessService mediaFileProcessService;

    @Autowired
    public  MediaFileService mediaFileService;

    @Value("${videoprocess.ffmpegpath}")
    private String ffmpegpath;

    /**
     * 分片广播任务
     */
    @XxlJob("videoJobHandler")
    public void shardingJobHandlershardingJobHandler() throws Exception {

        // 分片参数
        int shardIndex = XxlJobHelper.getShardIndex(); //执行器的序号
        int shardTotal = XxlJobHelper.getShardTotal(); //执行器的总数


        //确定CPU的核心数 根据核心数确定执行器每次要执行的任务数量
        int processors = Runtime.getRuntime().availableProcessors();
        //查询待处理任务
        List<MediaProcess> mediaProcessList = mediaFileProcessService.getMediaProcessList(shardTotal,shardIndex , processors);
        //size等于待处理的任务数量
        int size = mediaProcessList.size();
        if (size<=0){
            log.debug("取到的任务数{}",size);
        }

        //使用计数器  count等于待处理的任务数量
        CountDownLatch countDownLatch = new CountDownLatch(size);
        //创建线程池

        ExecutorService executorService = Executors.newFixedThreadPool(size);
        mediaProcessList.forEach(mediaProcess -> {
            //将任务加入到线程池
            executorService.execute(()->{
                try {
                    //任务id
                    Long taskId = mediaProcess.getId();
                    //fileId
                    String fileId = mediaProcess.getFileId();
                    //开启任务
                    boolean b = mediaFileProcessService.startTask(taskId);
                    if (!b) {
                        log.debug("抢占失败,任务id:{}", taskId);
                        return;
                    }
                    //桶
                    String bucket = mediaProcess.getBucket();
                    //FilePath  avi文件在桶内的相对路径
                    String FilePath = mediaProcess.getFilePath();
                    //下载minio文件到本地
                    File file = mediaFileService.downloadFileFromMinIO(bucket, FilePath);
                    if (file == null) {
                        log.debug("下载视频出错，任务id:{},bucket:{},filePath:{}", taskId, bucket, FilePath);
                        //保存任务的失败结果
                        mediaFileProcessService.saveProcessFinishStatus(taskId, "3", fileId, null, "下载视频到本地失败");
                        return;
                    }
                    //源avi视频的路径
                    String video_path = file.getAbsolutePath();
                    //转码后的mp4文件名称
                    String mp4_name = fileId + ".mp4";
                    //转码后mp4的文件路径
                    File mp4_File = null;
                    try {
                        //先创建一个临时mp4文件保存转码后的视频
                        mp4_File = File.createTempFile("minio", ".mp4");

                    } catch (IOException e) {
                        log.debug("创建临时文件异常");
                        //保存处理任务失败的结果   更新mediafile表的url
                        mediaFileProcessService.saveProcessFinishStatus(taskId, "3", fileId, null, "创建临时文件异常");
                        return;
                    }
                    String mp4_path = mp4_File.getAbsolutePath();
                    //创建工具类对象
                    Mp4VideoUtil videoUtil = new Mp4VideoUtil(ffmpegpath, video_path, mp4_name, mp4_path);
                    String result = videoUtil.generateMp4();
                    if (!result.equals("success")) {
                        log.debug("视频转码失败，原因:{},bucket:{},filePath:{}", result, bucket, FilePath);
                        mediaFileProcessService.saveProcessFinishStatus(taskId, "3", fileId, null, result);
                        return;
                    }
                    //mp4文件保存在桶内相对路径
                   String absolutePath=getFilePathByMd5(fileId, ".mp4");
                    //上传minio mp4_path:临时文件 ，absolutePath  mp4文件在桶内的相对位置
                    boolean b1 = mediaFileService.addMediaFilesToMinIO(mp4_path, "video/mp4", bucket, absolutePath);
                    if (!b1) {
                        log.debug("上传mp4到minio失败,taskId:{}", taskId);
                        mediaFileProcessService.saveProcessFinishStatus(taskId, "3", fileId, null, "上传mp4到minio失败");
                        return;
                    }
                    //MP4文件的url
                    String url = "/"+bucket+"/"+ absolutePath;

                    //跟更新任务状态成功
                    mediaFileProcessService.saveProcessFinishStatus(taskId, "4", fileId, url, "null");
                }finally {
                 //计数器减一
                    countDownLatch.countDown();
                }
            });
        });
      //阻塞,指定最大限制时间，阻塞最多等待30分钟
        countDownLatch.await(30, TimeUnit.MINUTES);

    }
    //根据MD5获取文件minio文件地址
    private String getFilePathByMd5(String fileMd5,String fileExt) {
        return fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) + "/" + fileMd5 + "/" + fileMd5+fileExt;
    }

}
